In [ ]:
%%HTML
<style>
.container { width:100% !important }
</style>
In [ ]:
def search(start, goal, next_states):
FrontierA = { start }
VisitedA = set() # set of nodes expanded starting from start
ParentA = { start: start}
FrontierB = { goal }
VisitedB = set() # set of nodes expanded starting from goal
ParentB = { goal: goal}
while len(FrontierA) > 0 and len(FrontierB) > 0:
VisitedA |= FrontierA
VisitedB |= FrontierB
print(len(VisitedA) +len(VisitedB))
NewFrontier = set()
for s in FrontierA:
for ns in next_states(s):
if ns not in VisitedA:
NewFrontier |= { ns }
ParentA[ns] = s
if ns in VisitedB:
return combinePaths(ns, ParentA, ParentB)
FrontierA = NewFrontier
NewFrontier = set()
for s in FrontierB:
for ns in next_states(s):
if ns not in VisitedB:
NewFrontier |= { ns }
ParentB[ns] = s
if ns in VisitedA:
return combinePaths(ns, ParentA, ParentB)
FrontierB = NewFrontier
Given a state
and a parent dictionary Parent
, the function path_to
returns a path leading to the given state
.
In [ ]:
def path_to(state, Parent):
p = Parent[state]
if p == state:
return [state]
return path_to(p, Parent) + [state]
The function combinePath
takes three parameters:
state
is a state that has been reached in bidirectional BFS from both start
and goal
.ParentA
is the parent dictionary that has been build when searching from start
.
If $\texttt{ParentA}[s_1] = s_2$ holds, then either $s_1 = s2 = \texttt{start}$ or
$s_1 \in \texttt{next_states}(s_2)$.ParentB
is the parent dictionary that has been build when searching from goal
.
If $\texttt{ParentB}[s_1] = s_2$ holds, then either $s_1 = s2 = \texttt{goal}$ or
$s_1 \in \texttt{next_states}(s_2)$.
The function returns a path from start
to goal
.
In [ ]:
def combinePaths(state, ParentA, ParentB):
Path1 = path_to(state, ParentA)
Path2 = path_to(state, ParentB)
return Path1[:-1] + Path2[::-1] # Path2 is reversed
In [ ]:
%run Sliding-Puzzle.ipynb
In [ ]:
%%time
Path = search(start, goal, next_states)
print(len(Path)-1)
In [ ]:
animation(Path)
In [ ]: